1 module features.android_ndk;
2 public import feature;
3 import commons;
4 enum TargetAndroidNDK = "28.0.12433566";
5 
6 Feature AndroidNDKFeature;
7 
8 
9 
10 enum FindAndroidNdkResult
11 {
12 	NotFound,
13 	Found,
14 	MustInstallSdk,
15 	MustInstallNdk
16 }
17 
18 private FindAndroidNdkResult tryFindAndroidNDK(ref Terminal t, ref RealTimeConsoleInput input)
19 {
20 	if("ANDROID_NDK_HOME" in environment)
21 	{
22 		configs["androidNdkPath"] = environment["ANDROID_NDK_HOME"];
23 		string sdk = getFirstExistingVar("ANDROID_SDK", "ANDROID_SDK_HOME");
24 		if(!sdk.length)
25 			sdk = buildNormalizedPath(configs["androidNdkPath"].str, "..", "..");
26 		configs["androidSdkPath"] = sdk;
27 		return FindAndroidNdkResult.Found;
28 	}
29 	bool isValidNDK(string chosenNDK)
30 	{
31 		import std.conv:to;
32 		int ndkVer = chosenNDK[0..2].to!int;
33 		return ndkVer <= 21;
34 	}
35 	version(Windows)
36 	{
37 		string locAppData = environment["LOCALAPPDATA"];
38 		if(locAppData == null)
39 		{
40 			t.writelnError("Could not find %LOCALAPPDATA% in your Windows.");
41 			t.flush;
42 			return FindAndroidNdkResult.NotFound;
43 		}
44 		string sdkPath = buildNormalizedPath(locAppData, "Android", "Sdk");
45 		string tempNdkPath = sdkPath;
46 
47 		if(!std.file.exists(sdkPath))
48 		{
49 			t.writelnError("Could not find ", sdkPath, ". You need to install Android SDK.");
50 			t.flush;
51 			return FindAndroidNdkResult.MustInstallSdk;
52 		}
53 		tempNdkPath = buildNormalizedPath(sdkPath, "ndk");
54 		if(!std.file.exists(tempNdkPath))
55 		{
56 			t.writelnError("Could not find ", tempNdkPath, ". You need to have at least one NDK installed.");
57 			t.flush;
58 			return FindAndroidNdkResult.MustInstallNdk;
59 		}
60 		do
61 		{
62 			string ndkPath = selectInFolder(
63 				"Select the NDK which you want to use. Remember that only NDK <= 21 is supported.",
64 				tempNdkPath, t, input
65 			);
66 			if(isValidNDK(ndkPath))
67 			{
68 				tempNdkPath = ndkPath;
69 				break;
70 			}
71 			t.writelnError("Please select a valid NDK (<= 21)");
72 		} while(true);
73 		t.writelnSuccess("Chosen "~ tempNdkPath~ " as your NDK.");
74 		environment["androidNdkPath"] = sdkPath;
75 		environment["androidSdkPath"] = tempNdkPath;
76 		return FindAndroidNdkResult.Found;
77 	}
78 	else version(linux)
79 	{
80 		return FindAndroidNdkResult.NotFound;
81 	}
82 	else version(OSX)
83 	{
84 		return FindAndroidNdkResult.NotFound;
85 	}
86 }
87 
88 
89 private bool installAndroidNDK(ref Terminal t, ref RealTimeConsoleInput input, TargetVersion ver, Download[] content)
90 {
91     string finalOutput = buildNormalizedPath(configs["androidSdkPath"].str, "cmdline-tools", "latest");
92 	string sdkManagerPath = buildNormalizedPath(finalOutput, "bin");
93 	string execSdkManager = "sdkmanager ";
94 	version(Posix) execSdkManager = "./sdkmanager";
95     string packagesToInstall = `"ndk;`~ver.toString~`" `;
96 
97 	t.writelnHighlighted("Installing packages: ", packagesToInstall, " \n\t", "You may need to accept some permissions, this process may take a little bit of time.");
98     t.flush;
99 	if(t.wait(spawnShell("cd "~sdkManagerPath~" && "~execSdkManager ~" " ~packagesToInstall)) != 0)
100 	{
101 		t.writeln("Failed on installing NDK.");
102 		t.flush;
103 		return false;
104 	}
105 
106 	
107 	configs["androidNdkPath"] = buildNormalizedPath(configs["androidSdkPath"].str, "ndk", ver.toString);
108 	version(linux)
109 	{
110 		string binPath = buildNormalizedPath(configs["androidNdkPath"].str, "toolchains", "llvm", "prebuilt", "linux-x86_64", "bin");
111 		makeFileExecutable(buildNormalizedPath(binPath, "aarch64-linux-android35-clang"));
112 		makeFileExecutable(buildNormalizedPath(binPath, "ld.lld"));
113 	}
114 	updateConfigFile();
115 	return true;
116 }
117 
118 void initialize()
119 {
120     AndroidNDKFeature = Feature(
121         "Android NDK",
122         "Android Native Development Kit. Required for developing in other languages than Java/Kotlin for Android",
123         ExistenceChecker(["androidNdkPath"], null),
124         Installation(null, toDelegate(&installAndroidNDK)),
125         null,
126         VersionRange.parse(TargetAndroidNDK)
127     );
128 }
129 void start()
130 {
131     import features.android_sdk;
132     AndroidNDKFeature.dependencies = [&AndroidSDKFeature];
133 }